//-------------------------------------------------------------------
//	Copyright (c) 2010-2011  Zhirnov Andrey
//	This file is part of the "UXGEN_ResourceConverter" project.
//	See copyright notice in "Converter.h".
//-------------------------------------------------------------------

#include "ILWrapper.h"
#include "..\\Core\\CConverter.h"

#pragma comment (lib, "..\\..\\Libs\\IL\\DevIL.lib")
#pragma comment (lib, "..\\..\\Libs\\IL\\ILU.lib")


ILWrapper::ILWrapper(): _uImage(0)
{
	static bool	s_b_inited = false;

	if ( s_b_inited )
		return;

	if ( ilGetInteger( IL_VERSION_NUM ) < IL_VERSION )
	{
		LOG( "Incorrect DevIL.dll version.", );
		return;
	}

	if ( iluGetInteger( ILU_VERSION_NUM ) < ILU_VERSION )
	{
		LOG( "Incorrect ILU.dll version.", );
		return;
	}

	
	ilInit();
	iluInit();

	ilEnable( IL_KEEP_DXTC_DATA );
	ilSetInteger( IL_KEEP_DXTC_DATA, IL_TRUE );

	s_b_inited = true;
}


ILWrapper::~ILWrapper()
{
}
	

bool ILWrapper::ConvertILFormat(ILint fmt, ILint type, ILint dxfmt, int channels, e_tex_format::type &eFormat, e_tex_swizzle::type &eSwizzle)
{
	GLenum	gl_fmt	= 0,
			gl_ifmt	= 0,
			gl_type	= 0;
	GLint	swizzle[4]	= { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA };

	switch ( fmt )
	{
		case IL_ALPHA :
			gl_fmt	= GL_RED;
			gl_ifmt	= GL_RED;
			swizzle[0] = GL_ONE;
			swizzle[1] = GL_ONE;
			swizzle[2] = GL_ONE;
			swizzle[3] = GL_RED;
			break;
		
		case IL_RGB :
			gl_fmt	= GL_RGB;
			gl_ifmt	= GL_RGB;
			swizzle[3] = GL_ONE;
			break;
		
		case IL_RGBA :
			gl_fmt	= GL_RGBA;
			gl_ifmt	= GL_RGBA;
			break;
		
		case IL_BGR :
			gl_fmt	= GL_BGR;
			gl_ifmt	= GL_RGB;
			swizzle[3] = GL_ONE;
			break;
		
		case IL_BGRA :
			gl_fmt	= GL_BGRA;
			gl_ifmt	= GL_RGBA;
			break;
		
		default :
			{
				bool	b_res = false;

				switch ( channels )
				{
					case 1 :
						b_res = ilConvertImage( IL_ALPHA, type ) == IL_TRUE;
						fmt		= IL_ALPHA;
						gl_fmt	= GL_RED;
						gl_ifmt	= GL_RED;
						swizzle[0] = GL_ONE;
						swizzle[1] = GL_ONE;
						swizzle[2] = GL_ONE;
						swizzle[3] = GL_RED;
						break;

					case 2 :
						b_res = ilConvertImage( IL_RGB, type ) == IL_TRUE;
						fmt		= IL_RGB;
						gl_fmt	= GL_RGB;
						gl_ifmt	= GL_RG;
						swizzle[2] = GL_ONE;
						swizzle[3] = GL_ONE;
						break;

					case 3 :
						b_res = ilConvertImage( IL_RGB, type ) == IL_TRUE;
						fmt		= IL_RGB;
						gl_fmt	= GL_RGB;
						gl_ifmt	= GL_RGB;
						swizzle[3] = GL_ONE;
						break;

					case 4 :
						b_res = ilConvertImage( IL_RGBA, type ) == IL_TRUE;
						fmt		= IL_RGBA;
						gl_fmt	= GL_RGBA;
						gl_ifmt	= GL_RGBA;
						break;
				};

				if ( !b_res ) {
					LOG( (string("Unknown color format, code: ") << fmt << '.').cstr(), );
					return false;
				}
			}
	}

	switch ( type )
	{
		case IL_BYTE :
			gl_type	= GL_BYTE;
			break;

		case IL_UNSIGNED_BYTE :
			gl_type = GL_UNSIGNED_BYTE;
			break;

		case IL_SHORT :
			gl_type = GL_SHORT;
			break;

		case IL_UNSIGNED_SHORT :
			gl_type = GL_UNSIGNED_SHORT;
			break;

		case IL_INT :
			gl_type = GL_INT;
			break;

		case IL_UNSIGNED_INT :
			gl_type = GL_UNSIGNED_INT;
			break;

		case IL_FLOAT :
			gl_type = GL_FLOAT;
			break;

		case IL_HALF :
			gl_type = GL_HALF_FLOAT;
			break;

		case IL_DOUBLE :
			gl_type = GL_DOUBLE;
			break;

		default :
			LOG( (string("Unknown color type, code: ") << type << '.').cstr(), );
			return false;
	}

	if ( dxfmt != 0 && dxfmt != IL_DXT_NO_COMP )
	{
		if ( gl_ifmt == GL_RGB || dxfmt == IL_DXT1 )
			gl_ifmt = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
		else

		if ( gl_ifmt == GL_RGBA )
		{
			if ( dxfmt == IL_DXT1 )	gl_ifmt = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;		else
			if ( dxfmt == IL_DXT3 )	gl_ifmt = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;		else
			if ( dxfmt == IL_DXT5 )	gl_ifmt = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;		else
				return false;
		}
		else
		{
			LOG( "Unsupported DXT format.", );
			return false;
		}
	}

	if ( Image::DeconvertFormat( gl_ifmt, gl_fmt, gl_type, eFormat ) ) {
		if ( !Image::SetSwizzle( swizzle, eSwizzle ) )
		{
			return Image::AutoSwizzle( eFormat, eSwizzle );
		}
		return true;
	}
	return false;
}


bool ILWrapper::LoadTexture(const char *pcFileName, E_TEXTURE_TYPE &eType)
{
	DeleteTexture();

	eType = TT_NONE;

	ILuint	texId;

	ilGenImages( 1, &texId );
	ilBindImage( texId );

	if ( ilLoadImage( pcFileName ) != IL_TRUE ) {
		ilBindImage( 0 );
		ilDeleteImages( 1, &texId );
		LOG( "Can't load image.", );
		return false;
	}

	_uImage = texId;


	// switch image type //
	int		width	= ilGetInteger( IL_IMAGE_WIDTH  );
	int		height	= ilGetInteger( IL_IMAGE_HEIGHT );
	int		depth	= ilGetInteger( IL_IMAGE_DEPTH );
	int		layers	= ilGetInteger( IL_NUM_LAYERS );
	int		cube	= ilGetInteger( IL_IMAGE_CUBEFLAGS );
	int		imgnum	= ilGetInteger( IL_NUM_IMAGES );
	bool	ret		= false;

	const char *p_ext = String::GetFileExt( pcFileName );

	if ( //stricmp( p_ext, "bmp" ) == 0 ||
		 stricmp( p_ext, "jpg" ) == 0 )
	{
		iluFlipImage();
	}

	if ( depth == 1 )
	{
		if ( width == 1 || height == 1 )
		{
			// texture 1D
			eType = TT_TEXTURE_1D;
			ret   = true;
		}
		else
		{
			// texture 2D, cube map, texture 2d array
			if ( cube == 0 && layers == 0 ) {
				eType = TT_TEXTURE_2D;
				ret   = true;
			}
			else
			if ( cube != 0 && layers == 0 ) {
				eType = TT_TEXTURE_CUBE_MAP;
				ret   = true;
			}
			else
			if ( cube == 0 && layers != 0 ) {
				eType = TT_TEXTURE_2D_ARRAY;
				ret   = true;
			}
			else
			if ( cube != 0 && layers != 0 ) {
				eType = TT_TEXTURE_CUBE_MAP_ARRAY;
				ret   = true;
			}
		}
	}
	else
	{
		// texture 3d
		eType = TT_TEXTURE_3D;
		ret   = true;
	}

	if ( !ret )
	{
		LOG( "Unknown texture type.", );
	}
	else
		_eType = eType;

	ilBindImage( 0 );

	return ret;
}


void ILWrapper::DeleteTexture()
{
	if ( _uImage != 0 )
	{
		ilBindImage( 0 );
		ilDeleteImages( 1, &_uImage );
		_uImage = 0;
	}
	_eType = TT_NONE;
}


bool ILWrapper::GetTexture1D(TImage1D &sImage)
{
	return false;
}


bool ILWrapper::GetTexture2D(TImage2D &sImage)
{
	if ( _uImage == 0 )
		return false;

	ilBindImage( _uImage );

	int		width	= ilGetInteger( IL_IMAGE_WIDTH  );
	int		height	= ilGetInteger( IL_IMAGE_HEIGHT );
	int		type	= ilGetInteger( IL_IMAGE_TYPE   );
	int		fmt		= ilGetInteger( IL_IMAGE_FORMAT );
	int		bpp		= ilGetInteger( IL_IMAGE_BYTES_PER_PIXEL );
	int		dxtc	= ilGetInteger( IL_DXTC_DATA_FORMAT );
	int		mipmaps	= ilGetInteger( IL_NUM_MIPMAPS );
	int		channel = ilGetInteger( IL_IMAGE_CHANNELS );
	bool	comp	= (dxtc == IL_DXT1) || (dxtc == IL_DXT2) || (dxtc == IL_DXT3) || (dxtc == IL_DXT4) || (dxtc == IL_DXT5);
	
	if ( width == 0 || height == 0 )
		return false;

	sImage.Clear();
	
	if ( !ConvertILFormat( fmt, type, dxtc, channel, sImage.eFormat, sImage.eSwizzle ) )
	{
		ilBindImage( 0 );
		LOG( "Can't convert format.", );
		return false;
	}

	uint	u_mm_count	= GetPowOfTwo( max(width, height) );

	if ( u_mm_count != mipmaps )
	{
		if ( mipmaps != 0 )
			LOG( (string("Wrong mipmaps count: ") << mipmaps << ".").cstr(), );
		u_mm_count = 1;
	}

	sImage.aMipMaps.Resize( u_mm_count, false );
	sImage.vTexDim = uvec2( width, height );


	for (uint i = 0; i < sImage.aMipMaps.Count(); ++i)
	{
		ilActiveMipmap(i);

		uint	size	= ilGetInteger( IL_IMAGE_SIZE_OF_DATA );
		uint8 *	data	= ilGetData();

		sImage.aMipMaps[i] = size;
		sImage.aData.Append( data, data + size );
		
		if ( size != bpp * width * height )
		{
			LOG( "data size not equal to expected value", );
		}

		width	= width  > 1 ? width  >>= 1 : 1;
		height	= height > 1 ? height >>= 1 : 1;
	}

	ilBindImage( 0 );

	return true;
}


bool ILWrapper::GetTexture3D(TImage3D &sImage)
{
	if ( _uImage == 0 )
		return false;

	ilBindImage( _uImage );

	int		width	= ilGetInteger( IL_IMAGE_WIDTH  );
	int		height	= ilGetInteger( IL_IMAGE_HEIGHT );
	int		depth	= ilGetInteger( IL_IMAGE_DEPTH  );
	int		type	= ilGetInteger( IL_IMAGE_TYPE   );
	int		fmt		= ilGetInteger( IL_IMAGE_FORMAT );
	int		bpp		= ilGetInteger( IL_IMAGE_BYTES_PER_PIXEL );
	int		dxtc	= ilGetInteger( IL_DXTC_DATA_FORMAT );
	int		mipmaps	= ilGetInteger( IL_NUM_MIPMAPS );
	int		channel = ilGetInteger( IL_IMAGE_CHANNELS );
	bool	comp	= (dxtc == IL_DXT1) || (dxtc == IL_DXT2) || (dxtc == IL_DXT3) || (dxtc == IL_DXT4) || (dxtc == IL_DXT5);
	
	if ( width == 0 || height == 0 || depth == 0 )
		return false;

	sImage.Clear();
	
	if ( !ConvertILFormat( fmt, type, dxtc, channel, sImage.eFormat, sImage.eSwizzle ) )
	{
		ilBindImage( 0 );
		LOG( "Can't convert format.", );
		return false;
	}

	uint	u_mm_count	= GetPowOfTwo( max(width, max(height, depth)) );

	if ( u_mm_count != mipmaps )
	{
		if ( mipmaps != 0 )
			LOG( (string("Wrong mipmaps count: ") << mipmaps << ".").cstr(), );
		u_mm_count = 1;
	}

	sImage.aMipMaps.Resize( u_mm_count, false );
	sImage.vTexDim = uvec3( width, height, depth );

	for (uint i = 0; i < sImage.aMipMaps.Count(); ++i)
	{
		ilActiveMipmap(i);

		uint	size	= ilGetInteger( IL_IMAGE_SIZE_OF_DATA );
		uint8 *	data	= ilGetData();

		sImage.aMipMaps[i] = size;
		sImage.aData.Append( data, data + size );
		
		if ( size != bpp * width * height * depth )
		{
			LOG( "data size not equal to expected value", );
		}

		width	= width  > 1 ? width  >>= 1 : 1;
		height	= height > 1 ? height >>= 1 : 1;
		depth	= depth > 1  ? depth  >>= 1 : 1;
	}

	ilBindImage( 0 );

	return true;
}


bool ILWrapper::GetTextureCube(array<TImage2D> &aImages)
{
	if ( _uImage == 0 )
		return false;

	ilBindImage( _uImage );

	int		width	= ilGetInteger( IL_IMAGE_WIDTH  );
	int		height	= ilGetInteger( IL_IMAGE_HEIGHT );
	int		type	= ilGetInteger( IL_IMAGE_TYPE   );
	int		fmt		= ilGetInteger( IL_IMAGE_FORMAT );
	int		bpp		= ilGetInteger( IL_IMAGE_BYTES_PER_PIXEL );
	int		dxtc	= ilGetInteger( IL_DXTC_DATA_FORMAT );
	int		mipmaps	= ilGetInteger( IL_NUM_MIPMAPS );
	int		faces	= ilGetInteger( IL_NUM_FACES );
	int		channel = ilGetInteger( IL_IMAGE_CHANNELS );
	bool	comp	= (dxtc == IL_DXT1) || (dxtc == IL_DXT2) || (dxtc == IL_DXT3) || (dxtc == IL_DXT4) || (dxtc == IL_DXT5);
	
	if ( width == 0 || height == 0 || faces == 0 )
		return false;

	aImages.Clear();

	e_tex_format::type		e_format;
	e_tex_swizzle::type		e_swizzle;

	if ( !ConvertILFormat( fmt, type, dxtc, channel, e_format, e_swizzle ) )
	{
		ilBindImage( 0 );
		LOG( "Can't convert format.", );
		return false;
	}

	uint	u_mm_count	= GetPowOfTwo( max(width, height) );

	if ( u_mm_count != mipmaps )
	{
		if ( mipmaps != 0 )
			LOG( (string("Wrong mipmaps count: ") << mipmaps << ".").cstr(), );
		u_mm_count = 1;
	}

	aImages.Resize( faces );

	for (uint j = 0; j < aImages.Count(); ++j)
	{
		//ilActiveFace(j);
		
		aImages[j].aMipMaps.Resize( u_mm_count, false );
		aImages[j].vTexDim  = uvec2( width, height );
		aImages[j].eFormat	= e_format;
		aImages[j].eSwizzle	= e_swizzle;

		for (uint i = 0; i < aImages[j].aMipMaps.Count(); ++i)
		{
			ilActiveMipmap(i);

			uint	size	= ilGetInteger( IL_IMAGE_SIZE_OF_DATA );
			uint8 *	data	= ilGetData();

			aImages[j].aMipMaps[i] = size;
			aImages[j].aData.Append( data, data + size );
		
			if ( size != bpp * width * height )
			{
				LOG( "data size not equal to expected value", );
			}

			width	= width  > 1 ? width  >>= 1 : 1;
			height	= height > 1 ? height >>= 1 : 1;
		}
	}

	ilBindImage( 0 );

	return true;
}


bool ILWrapper::GetTexture2DArray(array<TImage2D> &aImages)
{
	if ( _uImage == 0 )
		return false;

	ilBindImage( _uImage );

	int		width	= ilGetInteger( IL_IMAGE_WIDTH  );
	int		height	= ilGetInteger( IL_IMAGE_HEIGHT );
	int		depth	= ilGetInteger( IL_IMAGE_DEPTH  );
	int		type	= ilGetInteger( IL_IMAGE_TYPE   );
	int		fmt		= ilGetInteger( IL_IMAGE_FORMAT );
	int		bpp		= ilGetInteger( IL_IMAGE_BYTES_PER_PIXEL );
	int		dxtc	= ilGetInteger( IL_DXTC_DATA_FORMAT );
	int		mipmaps	= ilGetInteger( IL_NUM_MIPMAPS );
	int		layers	= ilGetInteger( IL_NUM_LAYERS );
	int		channel = ilGetInteger( IL_IMAGE_CHANNELS );
	bool	comp	= (dxtc == IL_DXT1) || (dxtc == IL_DXT2) || (dxtc == IL_DXT3) || (dxtc == IL_DXT4) || (dxtc == IL_DXT5);
	
	if ( width == 0 || height == 0 || layers == 0 )
		return false;

	aImages.Clear();
	
	e_tex_format::type		e_format;
	e_tex_swizzle::type		e_swizzle;

	if ( !ConvertILFormat( fmt, type, dxtc, channel, e_format, e_swizzle ) )
	{
		ilBindImage( 0 );
		LOG( "Can't convert format.", );
		return false;
	}

	uint	u_mm_count	= GetPowOfTwo( max(width, height) );

	if ( u_mm_count != mipmaps )
	{
		if ( mipmaps != 0 )
			LOG( (string("Wrong mipmaps count: ") << mipmaps << ".").cstr(), );
		u_mm_count = 1;
	}

	aImages.Resize( layers );

	for (uint j = 0; j < aImages.Count(); ++j)
	{
		ilActiveLayer(j);

		aImages[j].aMipMaps.Resize( u_mm_count, false );
		aImages[j].vTexDim	= uvec2( width, height );
		aImages[j].eFormat	= e_format;
		aImages[j].eSwizzle	= e_swizzle;

		for (uint i = 0; i < aImages[j].aMipMaps.Count(); ++i)
		{
			ilActiveMipmap(i);

			uint	size	= ilGetInteger( IL_IMAGE_SIZE_OF_DATA );
			uint8 *	data	= ilGetData();

			aImages[j].aMipMaps[i] = size;
			aImages[j].aData.Append( data, data + size );
		
			if ( size != bpp * width * height )
			{
				LOG( "data size not equal to expected value", );
			}

			width	= width  > 1 ? width  >>= 1 : 1;
			height	= height > 1 ? height >>= 1 : 1;
		}
	}

	ilBindImage( 0 );

	return true;
}


bool ILWrapper::GetTexture1DArray(array<TImage1D> &aImages)
{
	return false;
}

